package cbfg.rvadapter.selection

import cbfg.rvadapter.RVAdapter
import cbfg.rvadapter.RVAdapter.Companion.RV_FLAG_CLEAR_SELECTIONS
import cbfg.rvadapter.RVAdapter.Companion.RV_FLAG_NOTIFY_CHANGED
import cbfg.rvadapter.RVAdapter.Companion.RV_PAYLOAD_DESELECTED
import cbfg.rvadapter.RVAdapter.Companion.RV_PAYLOAD_SELECTABLE
import cbfg.rvadapter.RVAdapter.Companion.RV_PAYLOAD_SELECTED
import cbfg.rvadapter.RVAdapter.Companion.RV_PAYLOAD_UNSELECTABLE
import cbfg.rvadapter.selection.store.IStore
import cbfg.rvadapter.selection.store.IndexMap
import cbfg.rvadapter.selection.store.MapStore
import cbfg.rvadapter.selection.store.SetStore
import cbfg.rvadapter.selection.store.SortType
import java.util.TreeSet

/**
 * 配置可选数据类型信息
 * @param singleSelectableArray 单选数据类型列表
 * @param multiSelectableArray 多选数据类型列表
 * @param enableSelectable 是否开启可选状态。默认不开启，后续可以通过 [enableSelectable] 或者 [enableAllSelectable] 方法开启。
 * @param strictCheckDataType 是否严格检查传入的数据类型是否是对应的或者可选的。默认不开启。
 * @param sort 排序类型。默认不排序。
 */
class SelectionDelegate<T : Any>(
    singleSelectableArray: Array<Class<*>> = emptyArray(),
    multiSelectableArray: Array<Class<*>> = emptyArray(),
    private var enableSelectable: Boolean = false,
    private val strictCheckDataType: Boolean = false,
    sort: SortType = SortType.NONE
) : ISelectionDelegate<T> {
    /** 记录数据类型单多选信息 */
    private val selectableTypeMap = HashMap<Class<*>, Boolean>()

    /** 选中项数据存储容器 */
    private val selectionStore: IStore<T>

    init {
        singleSelectableArray.forEach {
            selectableTypeMap[it] = false
        }
        multiSelectableArray.forEach {
            selectableTypeMap[it] = true
        }
        require(selectableTypeMap.isNotEmpty()) { "请在以下参数中配置可选的数据类型信息：singleSelectableArray 或 multiSelectableArray" }

        this.selectionStore = when (sort) {
            is SortType.NONE -> SetStore(HashSet())
            is SortType.IndexSort -> MapStore(IndexMap(sort.descending))
            is SortType.ItemSort<*> -> SetStore(TreeSet((sort as SortType.ItemSort<T>).comparator))
        }
    }

    private lateinit var adapter: RVAdapter<T>
    private lateinit var items: MutableList<T>

    override fun setup(adapter: RVAdapter<T>, mutableItems: MutableList<T>) {
        this.adapter = adapter
        this.items = mutableItems
    }

    override fun getSelections(): Collection<T> = selectionStore.items

    override val isSelectable: Boolean = enableSelectable

    /**
     * 设置所有配置过可选信息的数据类型 item 是否可选。
     * @param flags 可以是以下组合：
     * [RV_FLAG_CLEAR_SELECTIONS] 清空该数据类型的选中项
     * [RV_FLAG_NOTIFY_CHANGED] 刷新
     */
    override fun enableSelectable(selectable: Boolean, flags: Int) {
        if (enableSelectable == selectable) return

        enableSelectable = selectable
        //是否移除指定类型 item 选中项:
        if (flags.shouldClearSelections) {
            deselectAll(false)
        }
        //是否刷新数据:
        if (flags.shouldNotifyChanged) {
            adapter.notifyItemRangeChanged(
                0,
                items.size,
                if (selectable) RV_PAYLOAD_SELECTABLE else RV_PAYLOAD_UNSELECTABLE
            )
        }
    }

    override fun selectAt(index: Int) {
        require(enableSelectable) { TIP_SELECT_DISABLED }
        val element = items.getOrNull(index) ?: return
        element.checkElementSelectable
        add(element, index)
    }

    override fun selectRange(fromIndex: Int, toIndex: Int) {
        if (fromIndex >= toIndex) return
        if (fromIndex !in 0 until items.size) return
        if (toIndex !in 0 until items.size) return

        selectAll(items.subList(fromIndex, toIndex), IntRange(fromIndex, toIndex))
    }

    override fun selectAll(list: Collection<T>, range: IntRange?) {
        require(enableSelectable) { TIP_SELECT_DISABLED }
        if (list.isEmpty()) return

        if (strictCheckDataType) {
            if (range == null) {
                list.forEach {
                    it.checkElementSelectable
                    add(it, -1)
                }
            } else {
                list.forEachIndexed { index, it ->
                    it.checkElementSelectable
                    add(it, range.first + index)
                }
            }
        } else {
            //如果不严格检查数据类型，则只检查第一个 item 是否可选：
            val firstItem = list.first()
            firstItem.checkElementSelectable
            if (!firstItem.elementMultiSelectable) {
                val index = items.indexOf(firstItem)
                if (index != -1) {
                    add(firstItem, index)
                }
            } else if (range == null) {
                list.forEach { add(it, -1) }
            } else {
                list.forEachIndexed { index, it ->
                    add(it, range.first + index)
                }
            }
        }
    }

    override fun selectAll(clazz: Class<*>) {
        require(enableSelectable) { TIP_SELECT_DISABLED }
        clazz.checkSelectable
        //单选:
        if (!clazz.multiSelectable) {
            val index = items.indexOfFirst { it.javaClass == clazz }
            items.getOrNull(index)?.let { add(it, index) }
        } else { //多选:
            items.forEachIndexed { index, item ->
                if (item.javaClass == clazz) {
                    add(item, index)
                }
            }
        }
    }

    override fun selectAll() {
        require(enableSelectable) { TIP_SELECT_DISABLED }
        selectAll(items, items.indices)
    }

    /**
     * 添加选中项
     * 调用者需要保证数据类型是可选的。
     */
    private fun add(element: T, index: Int) {
        //单选的话先移除当前选中项:
        if (!element.elementMultiSelectable) {
            getSelections().firstOrNull { it.javaClass == element.javaClass }?.let {
                //已选中的话则不用处理了：
                if (it == element) return
                deselectAt(items.indexOf(it), true)
            }
        }
        if (selectionStore.add(element, index)) {
            adapter.notifyItemChanged(index, RV_PAYLOAD_SELECTED)
        }
    }

    override fun deselectAt(index: Int, needNotify: Boolean) {
        items.getOrNull(index)?.let {
            if (selectionStore.remove(it) && needNotify) {
                adapter.notifyItemChanged(index, RV_PAYLOAD_DESELECTED)
            }
        }
    }

    override fun deselect(item: T, needNotify: Boolean) {
        deselectAt(items.indexOf(item), needNotify)
    }

    override fun deselectAll(clazz: Class<*>?, range: IntRange?, needNotify: Boolean) {
        if (!enableSelectable || selectionStore.items.isEmpty()) return

        //如果 clazz 为 null 则清空所有选中项:
        if (clazz == null) {
            selectionStore.clear()
            if (needNotify) {
                if (range == null) {
                    adapter.notifyItemRangeChanged(0, items.size, RV_PAYLOAD_DESELECTED)
                } else {
                    adapter.notifyItemRangeChanged(range.first, range.last, RV_PAYLOAD_DESELECTED)
                }
            }
        } else {
            //遍历所有数据，找到指定类型的数据并取消选中:
            items.forEachIndexed { index, item ->
                if (item.javaClass == clazz) {
                    deselectAt(index, needNotify)
                }
            }
        }
    }

    override fun deselectAll(list: Collection<T>, range: IntRange?, needNotify: Boolean) {
        if (!enableSelectable || selectionStore.items.isEmpty()) return

        if (!needNotify) {
            list.forEach { selectionStore.remove(it) }
        } else if (range != null) {
            list.forEach { selectionStore.remove(it) }
            adapter.notifyItemRangeChanged(range.first, range.last, RV_PAYLOAD_DESELECTED)
        } else {
            list.forEach {
                if (selectionStore.remove(it)) {
                    deselectAt(items.indexOf(it), true)
                }
            }
        }
    }

    override fun toggleSelect(item: T, index: Int) {
        if (selectionStore.items.contains(item)) {
            deselectAt(index, true)
        } else {
            selectAt(index)
        }
    }

    override fun isSelected(item: T) = enableSelectable && selectionStore.items.contains(item)

    private inline val Int.shouldClearSelections: Boolean
        get() = (this and RV_FLAG_CLEAR_SELECTIONS) != 0

    private inline val Int.shouldNotifyChanged: Boolean
        get() = (this and RV_FLAG_NOTIFY_CHANGED) != 0

    private inline val Class<*>.multiSelectable: Boolean
        get() = selectableTypeMap[this] == true

    private inline val Any.elementMultiSelectable: Boolean
        get() = this.javaClass.multiSelectable

    private inline val Class<*>.checkSelectable: Unit
        get() = require(selectableTypeMap.containsKey(this)) { "${TIP_SELECT_DISABLED}:${this}" }

    private inline val Any.checkElementSelectable: Unit
        get() = this.javaClass.checkSelectable

    companion object {
        private const val TIP_SELECT_DISABLED =
            "此类型数据当前不可选中，请检查是否在 configSelectionInfo 方法中配置了此类型，或者是否有开启了可选状态！"
    }
}